//#define _USE_BOOST

#undef __DEPRECATED  /* suppress auto_ptr warning */

#include <iostream>
#include <stdexcept>
#include <string>
#include <vector>
#include <memory>
#include <algorithm>

using std::cout;
using std::endl;
using std::runtime_error;
using std::string;
using std::vector;
using std::auto_ptr;
using std::for_each;
using std::reverse;

#ifdef _USE_BOOST
#include <boost/smart_ptr.hpp>
using boost::scoped_ptr;
using boost::shared_ptr;
using boost::weak_ptr;
using boost::static_pointer_cast;
#else
/*
 * Compile with: -std=c++0x
 * This will add a macro: #define __GXX_EXPERIMENTAL_CXX0X__ 1
 */
/*
 * <tr1/memory> already includes <memory> for g++.
 * Refer to "Dual C++0x and TR1 Implementation":
 *   http://gcc.gnu.org/onlinedocs/libstdc++/manual/memory.html
 */
#include <tr1/memory>
using std::move;
using std::unique_ptr;  /* c++-0x, gcc-4.4 */
/* no scoped_ptr, maybe in tr2? */
using std::tr1::shared_ptr;  /* c++98, tr1 */
using std::tr1::weak_ptr;    /* c++98, tr1 */
using std::tr1::static_pointer_cast;  /* c++98, tr1 */
#endif

/* Test class */
class aclass {
public:
    aclass() {
        cout << "in aclass::ctor()" << endl;
    }
    ~aclass() {
        cout << "in aclass::dtor()" << endl;
    }
};

/************************************************************
 *                         auto_ptr                         *
 ************************************************************/

int *test_auto_ptr_exp() {
    auto_ptr<int> p(new int(1));
    throw runtime_error("auto_ptr test exception.");
    /* exception-safe, p is free even when an exception is thrown. */
    return p.get();
}

void test_auto_ptr_basic() {
    auto_ptr<int> p1(new int(1));
    auto_ptr<int> p2(new int(2));
    auto_ptr<int> p3(p1);
    auto_ptr<int> p4;
    p4 = p2;
    if (p1.get()) {  /* NULL */
        cout << "*p1=" << *p1 << endl;
    }
    if (p2.get()) {  /* NULL */
        cout << "*p2=" << *p2 << endl;
    }
    if (p3.get()) {  /* ownership transferred from p1 to p3 */
        cout << "*p3=" << *p3 << endl;
    }
    if (p4.get()) {  /* ownership transferred from p2 to p4 */
        cout << "*p4=" << *p4 << endl;
    }
    /* ERROR: void is a type of template specialization */
    //auto_ptr<void> ptr5(new int(3));
}

void test_auto_ptr_errors() {
    /* ERROR: statically allocated object */
    const char *str = "Hello";
    auto_ptr<const char> p1(str);
    /* ERROR: two auto_ptrs refer to the same object */
    int *pi = new int(5);
    auto_ptr<int> p2(pi);
    auto_ptr<int> p3(p2.get());
    p2.~auto_ptr();  /* now p3 is not available too */
    /* ERROR: hold a pointer to a dynamically allocated array */
    /* When destroyed, it only deletes first single object. */
    auto_ptr<int> (new int[10]);
    /* ERROR: store an auto_ptr in a container */
    //vector<auto_ptr<int> > vec;
    //vec.push_back(auto_ptr<int>(new int(1)));
    //vec.push_back(auto_ptr<int>(new int(2)));
    //auto_ptr<int> p4(vec[0]);  /* vec[0] is assigned NULL */
    //auto_ptr<int> p5;
    //p5 = vec[1];  /* vec[1] is assigned NULL */
}

#ifdef _USE_BOOST

/************************************************************
 *                        scoped_ptr                        *
 ************************************************************/

void test_scoped_ptr() {
    /* simple solution for simple needs */
    scoped_ptr<aclass> p(new aclass);
}

#else

/************************************************************
 *                        unique_ptr                        *
 ************************************************************/

unique_ptr<int> get_unique_ptr(int i) {
    return unique_ptr<int> (new int(i));
}

void use_unique_ptr(unique_ptr<int> p) {
    /* p is deleted when finish running this function. */
}

struct aclass_deleter {
    void operator()(void *p) {
        delete static_cast<aclass *> (p);
    }
};

template<class T>
struct array_deleter {
    void operator()(T *array) {
        delete[] array;
    }
};

typedef array_deleter<aclass> aclass_array_deleter;
typedef unique_ptr<aclass, aclass_array_deleter> aclass_array_ptr;

void test_unique_ptr_basic() {
    unique_ptr<int> p(new int(1));
    /*
     * One can make a copy of an rvalue unique_ptr.
     * But one can not make a copy of an lvalue unique_ptr.
     * Note the defaulted and deleted functions usage in source code(c++0x).
     */
    //unique_ptr<int> p2 = p;  /* error */
    //use_unique_ptr(p);       /* error */
    use_unique_ptr(move(p));
    use_unique_ptr(get_unique_ptr(3));
}

void test_unique_ptr_custom_deleter() {
    /*
     * Hold a pointer to a dynamically allocated array.
     * aaptr & aaptr2 are deleted when finish running this function.
     */
    aclass_array_ptr aaptr(new aclass[3]);
    unique_ptr<aclass[]> aaptr2(new aclass[3]);  /* default_deleter<T[]> */
    /* allow void pointer, but a custom deleter must be used. */
    unique_ptr<void, aclass_deleter> p3(new aclass);
}

#endif

/************************************************************
 *                        shared_ptr                        *
 ************************************************************/

struct bclass {
    int i;
    bclass(int i) { this->i = i; }
    virtual ~bclass() {
        cout << "in bclass::dtor() with i=" << i << endl;
    }
};

struct cclass: bclass {
    cclass(int i) : bclass(i) { }
    virtual ~cclass() {
        cout << "in cclass::dtor() with i=" << i << endl;
    }
};

typedef shared_ptr<bclass> bclass_ptr;

struct bclass_ops {
    void operator()(const bclass_ptr& p) {
        cout << p->i << endl;
    }
    bool operator()(const bclass_ptr& a, const bclass_ptr& b) {
        return a->i < b->i;
    }
};

void use_shared_ptr(shared_ptr<int> p) {
    cout << "count=" << p.use_count() << endl;
}

void test_shared_ptr_basic() {
    shared_ptr<int> p;
    cout << "count=" << p.use_count() << endl;
    p.reset(new int(1));
    cout << "count=" << p.use_count() << endl;
    shared_ptr<int> p2 = p;
    cout << "count=" << p.use_count() << endl;
    use_shared_ptr(p2);
    cout << "count=" << p.use_count() << endl;
    p2.~shared_ptr();
    cout << "count=" << p.use_count() << endl;
    p2.~shared_ptr();
    cout << "count=" << p.use_count() << endl;
}

void test_shared_ptr_convertion() {
    /* p is deleted accurately without custom deleter */
    shared_ptr<void> p(new aclass);
    /* use parent type to hold child object */
    shared_ptr<bclass> p2(new cclass(10));
    shared_ptr<cclass> p3 = static_pointer_cast<cclass> (p2);
    cout << "p3->i=" << p3->i << endl;
    p3->i = 20;
    cout << "p2->i=" << p2->i << endl;
}

void test_shared_ptr_containers() {
    vector<bclass_ptr> vec1, vec2;
    bclass_ptr ptr(new bclass(1));
    vec1.push_back(ptr);
    vec2.push_back(ptr);
    ptr.reset(new bclass(2));
    vec1.push_back(ptr);
    vec2.push_back(ptr);
    ptr.reset(new bclass(3));
    vec1.push_back(ptr);
    vec2.push_back(ptr);
    for_each(vec1.begin(), vec1.end(), bclass_ops());
    reverse(vec2.begin(), vec2.end());
    for_each(vec2.begin(), vec2.end(), bclass_ops());
}

void test_shared_ptr_multithread() {
    // TODO: add contents
}

/************************************************************
 *                         weak_ptr                         *
 ************************************************************/

struct mynode {
    int i;
    shared_ptr<mynode> snext;
    weak_ptr<mynode> wnext;
    mynode(int i) { this->i = i; }
    ~mynode() { cout << "in mynode::dtor() with i=" << i << endl; }
};

void test_weak_ptr() {
    shared_ptr<mynode> head(new mynode(1));
    head->snext = shared_ptr<mynode>(new mynode(2));
    /* use weak_ptr to solve cyclic dependency */
    //head->snext = head;
    head->wnext = head;
}

#if 1
int main() {
#ifdef _USE_BOOST
    cout << "*** scoped_ptr ***" << endl;
    cout <<"test_scoped_ptr:" << endl;
    test_scoped_ptr();
    cout << endl;
#else
    cout << "*** unique_ptr ***" << endl;
    cout << "test_unique_ptr_basic:" << endl;
    test_unique_ptr_basic();
    cout << "test_unique_ptr_custom_deleter:" << endl;
    test_unique_ptr_custom_deleter();
    cout << endl;
#endif
    cout << "*** shared_ptr ***" << endl;
    cout << "test_shared_ptr_basic:" << endl;
    test_shared_ptr_basic();
    cout << "test_shared_ptr_convertion:" << endl;
    test_shared_ptr_convertion();
    cout << "test_shared_ptr_containers:" << endl;
    test_shared_ptr_containers();
    cout << "test_shared_ptr_multithread:" << endl;
    test_shared_ptr_multithread();
    cout << endl;

    cout << "***  weak_ptr  ***" << endl;
    test_weak_ptr();
    cout << endl;

    return 0;
}
#endif
